package org.modelcc.parser.fence;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.modelcc.language.syntax.Rule;
import org.modelcc.language.syntax.Symbol;
import org.modelcc.language.syntax.SyntaxConstraints;

/**
 * Fence precedence constraints
 * 
 * @author Luis Quesada (lquesada@modelcc.org), refactored by Fernando Berzal (fberzal@modelcc.org)
 */

public class FencePrecedenceConstraints 
{

	private FenceConstraintEnforcer fence;
	
	private SyntaxConstraints constraints;
	
    private Set<Symbol> toremove;

    
	public FencePrecedenceConstraints (FenceConstraintEnforcer fence, SyntaxConstraints constraints)
	{
		this.fence = fence;
		this.constraints = constraints;
        this.toremove = new HashSet<Symbol>();
	}
		
	
	public Set<Symbol> select (Set<Symbol> hs) 
	{
		Set<Symbol> hsf = new HashSet<Symbol>();

		while (!hs.isEmpty()) {
		    Set<Rule> precf = new HashSet<Rule>();
		    Set<Rule> precall = new HashSet<Rule>();

		    for (Symbol hs2: hsf) {
		        Set<Rule> aux1 = constraints.getSelectionPrecedences(hs2.getRule());
		        if (aux1 != null) {
		            precf.addAll(aux1);
		            precall.addAll(aux1);
		        }
		        Set<Rule> aux2 = constraints.getSelectionPrecedences(hs2.getRelevantRule());
		        if (aux2 != null) {
		            precf.addAll(aux2);
		            precall.addAll(aux2);
		        }
		    }

		    for (Symbol hs2: hs) {
		        Set<Rule> aux1 = constraints.getSelectionPrecedences(hs2.getRule());
		        if (aux1 != null) {
		            precall.addAll(aux1);
		        }
		        Set<Rule> aux2 = constraints.getSelectionPrecedences(hs2.getRelevantRule());
		        if (aux2 != null) {
		            precall.addAll(aux2);
		        }
		    }

		    for (Iterator<Symbol> ites = hs.iterator();ites.hasNext();) {
		        Symbol hs2 = ites.next();
		        if (precf.contains(hs2.getRule()) || precf.contains(hs2.getRelevantRule())) {
		        	ites.remove();
		        } else if (!precall.contains(hs2.getRule()) && !precall.contains(hs2.getRelevantRule())) {
		        	ites.remove();
		        	hsf.add(hs2);
		        }
		    }                
		}
		
		return hsf;
	}
	
	
	public void process (Rule rule, Symbol s) 
	{
		Set<Rule> ruleConstMe = constraints.getStartPrecedences(rule);
		
		for (Symbol symbol: fence.getSymbolsAt(s.getStartIndex())) {
			// symbol.getStartIndex()==s.getStartIndex()
			if (symbol.getType().equals(s.getType())) {
				Set<Rule> ruleConst = constraints.getStartPrecedences(symbol.getRule());
				boolean meInRuleConst = false;
				if (ruleConst != null)
					if (ruleConst.contains(rule))
						meInRuleConst = true;
				boolean otherInRuleConst = false;
				if (ruleConstMe != null)
					if (ruleConstMe.contains(symbol.getRule()))
						otherInRuleConst = true;
				if (meInRuleConst) {
					toremove.add(s);
				}
				if (otherInRuleConst) {
					toremove.add(symbol);
				}
			}
		}
	}
	
	
	public void filter ()
	{
        for (Symbol s: fence.getSyntaxGraph().getSymbols()) {
            if (!toremove.contains(s)) {
                if (s.getRule() != null) {
                    if (!fence.postBuild(s.getRule(),s)) {
                        toremove.add(s);
                    }
                }
            }
        }
        
        for (Symbol s: toremove)
        	fence.removeSymbol(s);
	}
}
